package com.khstudio.filebrowser.duallist;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;

import com.khstudio.filebrowser.R;
import com.khstudio.filebrowser.duallist.KhFileBrowserItem.TYPE;
import com.khstudio.filebrowser.duallist.KhFileBrowserView.onFileBrowserListener;
import com.khstudio.filebrowser.util.KhLog;

public class KhDualListLayout extends ViewGroup {
    public static final int MODE_NORMAL             = 0x0001;
    public static final int MODE_SELECT_FILE        = 0x0002;
    public static final int MODE_SELECT_FOLDER      = 0x0003;

    private static final int EVENT_NONE             = 0x0000;
    private static final int EVENT_DOWN             = 0x0001;
    private static final int EVENT_SCROLL           = 0x0002;
    private static final int EVENT_FLING            = 0x0003;

    private static final int FLING_CENTER           = 0x0001;
    private static final int FLING_LEFT             = 0x0002;
    private static final int FLING_RIGHT            = 0x0003;

    private Context             mContext;
    private int                 mMode;

    private KhFileBrowserView   mFolderView;
    private KhFileBrowserView   mFileView;
    private TextView            mEmptyView;
    private ImageView           mBoundaryView;

    private onKhDualListLayoutListener    mListener;

    private String  mPath;
    private String  mPathFolder;
    private int     mEvent;

    private int mBoundaryWidth;
    private int mBoundaryPos;
    private int mBoundaryMin;
    private int mBoundaryMax;
    private int mBoundaryMaxMg;
    private int mBoundaryGap;

    private int mScrollBase;
    private long mTransitionLoopTime;

    private boolean mInvalidate;

    private GestureDetector mGestureDetector;
    private int SCROLL_MIN_DISTANCE;
    private int FLING_MIN_DISTANCE;
    private int FLING_MAX_OFF_PATH;
    private int FLING_THRESHOLD_VELOCITY;
    private static final int FLING_DURATION = 300;

    public interface onKhDualListLayoutListener {
        void onFolderChange(String path);
        void onFileClick(String path);
        void onUpdateMenu();
    }

    public KhDualListLayout(Context context) {
        this(context, null);
    }

    public KhDualListLayout(Context context, AttributeSet attrs) {
        this(context, attrs , 0);
    }

    public KhDualListLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        mContext = context;
        mMode = MODE_NORMAL;
        mListener = null;

        setBackgroundResource(R.color.kh_duallist_bg_list_bg);

        mInvalidate = true;

        mGestureDetector = new GestureDetector(mContext, mGestureListener);

        SCROLL_MIN_DISTANCE = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, mContext.getResources().getDisplayMetrics());
        FLING_MIN_DISTANCE = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, mContext.getResources().getDisplayMetrics());
        FLING_MAX_OFF_PATH = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, mContext.getResources().getDisplayMetrics());
        FLING_THRESHOLD_VELOCITY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, mContext.getResources().getDisplayMetrics());

        mBoundaryPos = 0;
        mBoundaryMin = 0;
        mBoundaryMax = 0;
        mBoundaryGap = 0;

        mScrollBase = 0;
        mTransitionLoopTime = 0;

        _init();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int oWidthSize = MeasureSpec.getSize(widthMeasureSpec);
        int oWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        mBoundaryMax = oWidthSize - mBoundaryMaxMg - mBoundaryWidth;

        mBoundaryGap = mBoundaryMax - mBoundaryMin;

        int cWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mBoundaryMax, oWidthMode);

        if(mFolderView != null) {
            mFolderView.measure(cWidthMeasureSpec, heightMeasureSpec);
        }

        if(mFileView != null) {
            mFileView.measure(cWidthMeasureSpec, heightMeasureSpec);
            if(mEmptyView != null) {
                mEmptyView.measure(cWidthMeasureSpec, heightMeasureSpec);
            }
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(changed || mInvalidate) {
            int boundary = _onLayoutBoundary();

            float scaleD = (float)(boundary - mBoundaryMin) / (float) mBoundaryGap;
            scaleD = scaleD * 0.10f;

            mFolderView.setScaleX(0.90f + scaleD);
            mFolderView.setScaleY(0.90f + scaleD);
            mFolderView.layout(l, t, boundary, b);

            mBoundaryView.setScaleY(0.90f + scaleD);
            mBoundaryView.layout(boundary, t, boundary + mBoundaryWidth, b);

            mFileView.setScaleX(1.00f - scaleD);
            mFileView.setScaleY(1.00f - scaleD);
            mFileView.layout(boundary + mBoundaryWidth, t, r, b);

            mEmptyView.setScaleX(1.00f - scaleD);
            mEmptyView.setScaleY(1.00f - scaleD);
            mEmptyView.layout(boundary + mBoundaryWidth, t, r, b);
            mInvalidate = false;
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(mMode == MODE_SELECT_FOLDER) {
            return super.dispatchTouchEvent(ev);
        }

        mGestureDetector.onTouchEvent(ev);

        switch (ev.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            break;
        }

        return super.dispatchTouchEvent(ev);
    }

    public void setonKhDualListLayoutListener(onKhDualListLayoutListener l) {
        mListener = l;
    }

    public void init(String path) {
        mPath = path;
        mFolderView.refreshOnlyFolder(path);
        mFileView.refreshOnlyFile(path);
        _invalidate();
    }

    public void setFileSelectAll(boolean b) {
        mFileView.setSelectAll(b);
        _invalidate();
    }

    public boolean isFileSelectAll() {
        return mFileView.isSelectAll();
    }

    public int getFileSelectCount() {
        return mFileView.getSelectCount();
    }

    public int getFileSelectMax() {
        return mFileView.getFileSelectMax();
    }

    public String getPath() {
        if(mMode == MODE_SELECT_FOLDER) {
            return mPathFolder;
        }
        return mPath;
    }

    public ArrayList<String> getFileSelectList() {
        return mFileView.getSelectList();
    }

    public int getMode() {
        return mMode;
    }

//    public void addFolder(String folder) {
//        mFolderView.add(folder);
//    }

    public void modeBase() {
        if(mMode != MODE_NORMAL) {
            mMode = MODE_NORMAL;
            mFileView.setSelectDisable();
            mFileView.refreshOnlyFile(mPath);
            mFolderView.refreshOnlyFolder(mPath);
            _transitionStart(FLING_CENTER);
            _invalidate();
            if(mListener != null) mListener.onUpdateMenu();
        }
    }

    public void modeSelectFile() {
        if(mMode != MODE_SELECT_FILE) {
            mMode = MODE_SELECT_FILE;
            mFileView.setSelectEnable();
            _invalidate();

            if(mListener != null) mListener.onUpdateMenu();
        }
    }

    public void modeSelectFolder() {
        if(mMode != MODE_SELECT_FOLDER) {
            mMode = MODE_SELECT_FOLDER;
            mPathFolder = mPath;
            mFolderView.refresh(mPathFolder);

            ArrayList<String> selectList = mFileView.getSelectList();
            mFileView.refresh(selectList);
            mFileView.setSelectAll(true);

            _transitionStart(FLING_RIGHT);
            if(mListener != null) mListener.onUpdateMenu();
        }
    }

    public boolean modeBack() {
        if(mMode == MODE_SELECT_FILE) {
            mMode = MODE_NORMAL;
            mFileView.setSelectDisable();
            _invalidate();
            if(mListener != null) mListener.onUpdateMenu();
            return true;
        } else if(mMode == MODE_SELECT_FOLDER) {
            mMode = MODE_SELECT_FILE;

            ArrayList<String> selectList = mFileView.getSelectList();
            mFileView.refreshOnlyFile(mPath);
            mFileView.setSelect(selectList);

            mFolderView.refreshOnlyFolder(mPath);

            if(mListener != null) mListener.onUpdateMenu();
            return true;
        }
        return false;
    }

    private void _init() {
        mPath = null;

        int tmpBmWd = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher).getWidth();
        int tmpMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 11, mContext.getResources().getDisplayMetrics());

        mBoundaryMin = tmpBmWd + tmpMargin;
        mBoundaryMaxMg = tmpBmWd + tmpBmWd / 4 + tmpMargin;
        mBoundaryPos = mBoundaryMin;

        mFolderView = new KhFileBrowserView(mContext);
        mFolderView.setArrowVisble(View.GONE);
        mFolderView.setonFileBrowserListener(mFileBrowserListener);
        addView(mFolderView);

        mBoundaryView = new ImageView(mContext);
        mBoundaryView.setImageResource(R.drawable.kh_duallist_shape_boundary);
        mBoundaryView.setScaleType(ScaleType.FIT_XY);
        mBoundaryWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, mContext.getResources().getDisplayMetrics());
        addView(mBoundaryView);

        mEmptyView = new TextView(mContext);
        mEmptyView.setClickable(true);
        mEmptyView.setGravity(Gravity.CENTER);
        mEmptyView.setTypeface(null,Typeface.ITALIC);
        mEmptyView.setText("File is empty");
        mEmptyView.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 5, mContext.getResources().getDisplayMetrics()));
        addView(mEmptyView);

        mFileView = new KhFileBrowserView(mContext);
        mFileView.setEmptyView(mEmptyView);
        mFileView.setonFileBrowserListener(mFileBrowserListener);
        addView(mFileView);
    }

    private void _invalidate() {
        mInvalidate = true;
        invalidate();
    }

    private void _requestLayout() {
        mInvalidate = true;
        requestLayout();
    }

    private int _onLayoutBoundary() {
        if(mBoundaryPos < mBoundaryMin) {
            mBoundaryPos = mBoundaryMin;
        } else if(mBoundaryPos > mBoundaryMax) {
            mBoundaryPos = mBoundaryMax;
        }

        return mBoundaryPos;
    }

    private void _transitionStart(int msg) {
        mEvent = EVENT_FLING;
        mTransitionLoopTime = System.currentTimeMillis();

        mHandlerTransition.sendEmptyMessage(msg);
    }

    private onFileBrowserListener mFileBrowserListener = new onFileBrowserListener() {

        @Override
        public void onUpdate() {

        }

        @Override
        public void onClick(KhFileBrowserItem item) {
            if(mMode == MODE_NORMAL) {
                if(item.getType() == TYPE.UP || item.getType() == TYPE.SDCARD || item.getType() == TYPE.EXTSDCARD || item.getType() == TYPE.FOLDER) {
                    init(item.getPath());
                } else {
                    if(mListener != null) mListener.onFileClick(item.getPath());
                }
            } else if(mMode == MODE_SELECT_FILE) {
                if(item.getType() == TYPE.UP || item.getType() == TYPE.SDCARD || item.getType() == TYPE.EXTSDCARD || item.getType() == TYPE.FOLDER) {
                    init(item.getPath());
                }
                if(mListener != null) mListener.onUpdateMenu();
            } else if(mMode == MODE_SELECT_FOLDER) {
                if(item.getType() == TYPE.UP || item.getType() == TYPE.SDCARD || item.getType() == TYPE.EXTSDCARD || item.getType() == TYPE.FOLDER) {
                    mPathFolder = item.getPath();
                    mFolderView.refresh(mPathFolder);
                    _invalidate();
                }
            }
        }
    };

    private OnGestureListener mGestureListener = new OnGestureListener() {
        @Override
        public boolean onDown(MotionEvent e) {
            KhLog.i("onDown");
            mEvent = EVENT_DOWN;
            return false;
        }

        @Override
        public void onShowPress(MotionEvent e) {
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            KhLog.i("onScroll");
            if(mEvent == EVENT_DOWN) {
                if (Math.abs(e1.getY() - e2.getY()) > FLING_MAX_OFF_PATH) {
                    mEvent = EVENT_NONE;
                    return false;
                }

                if(Math.abs(e1.getX() - e2.getX()) > SCROLL_MIN_DISTANCE) {
                    mEvent = EVENT_SCROLL;
                    mScrollBase = (int)(e2.getX() - e1.getX());
                }
            }

            if(mEvent == EVENT_SCROLL) {
                mBoundaryPos = mBoundaryPos + (int)(e2.getX() - e1.getX()) - mScrollBase;
                mScrollBase = (int)(e2.getX() - e1.getX());
                _requestLayout();
            }

            return false;
        }

        @Override
        public void onLongPress(MotionEvent e) {
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (Math.abs(e1.getY() - e2.getY()) > FLING_MAX_OFF_PATH)
                return false;

            if(e1.getX() - e2.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_THRESHOLD_VELOCITY) {
                KhLog.i("onFling Left");
                _transitionStart(FLING_LEFT);
            } else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_THRESHOLD_VELOCITY) {
                KhLog.i("onFling Right");
                _transitionStart(FLING_RIGHT);
            }
            return false;
        }
    };

    private Handler mHandlerTransition = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(mEvent != EVENT_FLING) {
                return;
            }

            long loopTime = System.currentTimeMillis();
            double delay = loopTime - mTransitionLoopTime;
            mTransitionLoopTime = loopTime;

            switch (msg.what) {
                case FLING_CENTER: {
                    int half = (mBoundaryMax + mBoundaryMin + mBoundaryWidth) / 2;
                    if(mBoundaryPos < half) {
                        mBoundaryPos += (double) mBoundaryGap * (double) delay / (double) FLING_DURATION;
                        if(mBoundaryPos < half) {
                            mHandlerTransition.sendEmptyMessage(FLING_CENTER);
                        } else {
                            mBoundaryPos = half;
                        }
                        _requestLayout();
                    } else if(mBoundaryPos > half) {
                        mBoundaryPos -= (double) mBoundaryGap * (double) delay / (double) FLING_DURATION;
                        if(mBoundaryPos > half) {
                            mHandlerTransition.sendEmptyMessage(FLING_CENTER);
                        } else {
                            mBoundaryPos = half;
                        }
                        _requestLayout();
                    }
                }
                break;

                case FLING_LEFT: {
                    mBoundaryPos -= (double) mBoundaryGap * (double) delay / (double) FLING_DURATION;
                    _requestLayout();
                    if(mBoundaryPos > mBoundaryMin) {
                        mHandlerTransition.sendEmptyMessage(FLING_LEFT);
                    } else {

                    }
                }
                break;

                case FLING_RIGHT: {
                    mBoundaryPos += (double) mBoundaryGap * (double) delay / (double) FLING_DURATION;
                    _requestLayout();
                    if(mBoundaryPos < mBoundaryMax) {
                        mHandlerTransition.sendEmptyMessage(FLING_RIGHT);
                    } else {

                    }
                }
                break;
            }
        }
    };
}
